package org.spbsu.apco.server;

import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.bio.SocketConnector;
import org.eclipse.jetty.webapp.WebAppContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spbsu.apco.common.properties.ApplicationPropertiesManager;
import org.spbsu.apco.common.properties.PropertyKey;
import org.spbsu.apco.common.utils.StreamUtils;

import java.io.*;
import java.net.URL;
import java.util.concurrent.CountDownLatch;

public class EmbeddedServer {
    public static final Logger LOG = LoggerFactory.getLogger(EmbeddedServer.class);
    private static Server serverInstance;

    public static void main(String[] args) {
        if (serverInstance != null) {
            throw new RuntimeException("Won't start second server");
        }
        serverInstance = createServer();
        final CountDownLatch latch = new CountDownLatch(1);
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    serverInstance.start();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    latch.countDown();
                }
            }
        }).start();
        try {
            latch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException("Server initialization wan't awaited!");
        }
    }

    private static Server createServer() {
        final Server server = new Server();
        final SocketConnector connector = new SocketConnector();

        // Set some timeout options to make debugging easier.
        connector.setMaxIdleTime(1000 * 60 * 60);
        connector.setSoLingerTime(-1);

        connector.setHost(ApplicationPropertiesManager.getProperty(PropertyKey.BROKER_HOST));
        connector.setPort(Integer.valueOf(ApplicationPropertiesManager.getProperty(PropertyKey.BROKER_PORT)));
        server.setConnectors(new Connector[]{connector});

        final WebAppContext bb = new WebAppContext();
        bb.setServer(server);
        bb.setContextPath("/");
        bb.setWar(scanforWarPath());

        server.setHandler(bb);
        return server;
    }

    private static String scanforWarPath() {
        final String[] paths = {"../apco-server/src/main/webapp", "apco-server/src/main/webapp"};
        for (final String path : paths) {
            final File file = new File(path);
            if (file.exists()) {
                return path;
            }
        }

        try {
            return scanInJar();
        } catch (IOException e) {
            throw new RuntimeException("Can't find WAR files", e);
        }
    }

    private static String scanInJar() throws IOException {
        final File testFile = File.createTempFile("test", "");
        final File storage = testFile.getParentFile();
        testFile.delete();
        final File apco = new File(storage, "apco_server_config_" + System.currentTimeMillis());
        apco.mkdir();
        final File webapp = new File(apco, "webapp");
        webapp.mkdir();
        final File webinf = new File(webapp, "WEB-INF");
        webinf.mkdir();

        final String[] files = {"applicationContext.xml", "rest.service-servlet.xml", "web.xml", "server.xml"};
        for (final String file : files) {
            URL resource = EmbeddedServer.class.getResource(file);
            LOG.info("Adding resource: {}", resource);
            final InputStream resourceAsStream = EmbeddedServer.class.getResourceAsStream(file);
            final File newFile = new File(webinf, file);
            final OutputStream outputStream = new FileOutputStream(newFile);
            StreamUtils.transferData(resourceAsStream, outputStream);
        }

        return webapp.getAbsolutePath();
    }

    public static void stopServer() {
        try {
            serverInstance.stop();
            serverInstance.join();
        } catch (Exception e) {
            LOG.error("Can't stop server", e);
            throw new RuntimeException("Can't stop server", e);
        }
    }
}